﻿using System;
using System.Linq;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Controls;
using MahApps.Metro.Controls;
using Microsoft.VisualStudio.Shell;
using Microsoft.VSTalk.Integration;
using VSTalk.Engine;
using VSTalk.Engine.Core.EnvironmentManagers.UI;

namespace Microsoft.VSTalk
{
    /// <summary>
    /// This class implements the tool window exposed by this package and hosts a user control.
    ///
    /// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane, 
    /// usually implemented by the package implementer.
    ///
    /// This class derives from the ToolWindowPane class provided from the MPF in order to use its 
    /// implementation of the IVsUIElementPane interface.
    /// </summary>
    [Guid("4aaf9d89-bf38-4ed9-b0b5-1f324bfbbdc9")]
    public class BaseToolWindow : ToolWindowPane
    {
        private readonly FrameControl _control;

        private static IWindowManager WindowManager
        {
            get { return AppServices.Get<IWindowManager>(); }
        }

        private static IResourceStore ResourceStore
        {
            get { return WindowManager.ResourceStore; }
        }

        /// <summary>
        /// Standard constructor for the tool window.
        /// </summary>
        public BaseToolWindow() :
            base(null)
        {
            // Set the window title reading it from the resources.
            Caption = Resources.ToolWindowTitle;
            // Set the image that will appear on the tab of the window frame
            // when docked with an other window
            // The resource ID correspond to the one defined in the resx file
            // while the Index is the offset in the bitmap strip. Each image in
            // the strip being 16x16.
            BitmapResourceID = 301;
            BitmapIndex = 1;

            // This is the user control hosted by the tool window; Note that, even if this class implements IDisposable,
            // we are not calling Dispose on this object. This is because ToolWindowPane calls Dispose on 
            // the object returned by the Content property.
            _control = new FrameControl();

            _control.Loaded += ControlLoaded;

        }

        private void ControlLoaded(object sender, RoutedEventArgs e)
        {
            // ignore container with existence control
            if (_control.AddinContainer.Children.Count > 0)
            {
                return;
            }
            var manager = (EnvWindowsManager)WindowManager.EnvWindowsManager;
            if (manager == null)
            {
                return;
            }
            manager.SearchContent(this);
        }



        /// <summary>
        /// Called by package when creating a new tool window to set
        /// window content and pass on a Package ref
        /// </summary>
        /// <param name="userControl"></param>
        public void SetContent(UserControl userControl)
        {
            var addinContainer = _control.AddinContainer;
            MetroContentControl metroWrapper;
            if (!TryGetControl(userControl, out metroWrapper))
            {
                Caption = ExtractName(userControl);
                metroWrapper = new MetroContentControl();
                metroWrapper.Content = userControl;
                addinContainer.Children.Add(metroWrapper);
            }
            LoadResources(metroWrapper);
        }

        private bool TryGetControl(UserControl userControl, out MetroContentControl contentControl)
        {
            var metroContainer = _control.AddinContainer.Children.OfType<MetroContentControl>().FirstOrDefault();
            if (metroContainer != null)
            {
                var existedControl = metroContainer.Content;

                if (Equals(existedControl, userControl))
                {
                    contentControl = metroContainer;
                    return true;
                }
            }
            contentControl = null;
            return false;
        }

        private string ExtractName(UserControl control)
        {
            var hostedControlAttr = control.GetType()
                    .GetCustomAttributes(true)
                    .OfType<EnvControlInfoAttribute>()
                    .FirstOrDefault();
            if (hostedControlAttr == null)
            {
                return "Unknown";
            }
            return hostedControlAttr.Name;
        }

        private static void LoadResources(MetroContentControl metroWrapper)
        {
            foreach (var resource in ResourceStore.GetActualResources())
            {
                var resourceDictionary = metroWrapper.Resources;
                var mergedDictionaries = resourceDictionary.MergedDictionaries;
                mergedDictionaries.Add(resource);
            }
        }

        /// <summary>
        /// This property returns the control that should be hosted in the Tool Window.
        /// It can be either a FrameworkElement (for easy creation of toolwindows hosting WPF content), 
        /// or it can be an object implementing one of the IVsUIWPFElement or IVsUIWin32Element interfaces.
        /// </summary>
        override public object Content
        {
            get
            {
                return _control;
            }
        }
    }
}
